package jp.co.showbiz.common.util;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

public class IOUtil {

    /**
     * 모든 메소드는 static한 방법으로 접근하게 하기위해 default constructor를 private으로 막음.
     */
    private IOUtil() {
        // 사용안함
    }

    /**
     * InputStream 객체를 읽어서 String으로 리턴함.
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param bis 읽어들일 InputStream객체
     * @return InputStream으로부터 읽은 데이터의 내용
     * @throws IOException InputStream에 문제가 있거나, Stream으로부터 읽는과정중 문제가 발생하면 IOException을 던진다.
     */
    public static String readInputStream(InputStream bis) throws IOException {
        StringBuffer sb = new StringBuffer();

        if (bis.available() > 0) {
            byte[] b = new byte[1024];
            int count = 0;
            while (true) {
                count = bis.read(b);

                if (count <= -1) { // end of the stream
                    break;
                }

                sb.append(new String(b, 0, count));
            }
        }

        return sb.toString();
    }

    /**
     * 파일을 읽어 바이트의 배열로 리턴함
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param filePath 읽어들일 파일의 경로
     * @return 파일내용의 바이트 배열
     * @throws IOException 파일을 읽는도중 문제 발생하면 IOException을 던짐
     */
    public static byte[] readFileAsByteArray(String filePath) throws IOException {
        // RandomAccessFile raf = new RandomAccessFile(filePath, "r");
        // byte [] b = new byte[(int)raf.length()];
        // raf.read(b);
        // raf.close();

        ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
        FileInputStream fis = new FileInputStream(filePath);
        byte[] buffer = new byte[1024];

        int len;
        while ((len = fis.read(buffer)) > 0) {
            outStream.write(buffer, 0, len);
        }

        fis.close();
        outStream.close();

        return outStream.toByteArray();
    }

    /**
     * Reader 객체를 읽어서 String으로 리턴함.
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param reader 읽어들일 Reader객체
     * @return Reader로부터 읽은 데이터의 내용
     * @throws IOException Reader 에 문제가 있거나, Reader로부터 읽는과정중 문제가 발생하면 IOException을 던진다.
     */
    public static String readInputStream(Reader reader) throws IOException {
        StringBuffer sb = new StringBuffer();

        char[] c = new char[1024];
        int count = 0;
        while (true) {
            count = reader.read(c);

            if (count <= -1) { // end of the stream
                break;
            }

            sb.append(new String(c, 0, count));
        }

        return sb.toString();
    }

    public static ByteBuffer readFileToBuffer(String filePath) throws IOException {
        RandomAccessFile randomFile = null;
        ByteBuffer mapBuf = null;

        randomFile = new RandomAccessFile(filePath, "r");
        mapBuf = randomFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomFile.length());

        return mapBuf;
    }

    public static String readFile(String filePath) throws IOException {
        Charset charset = Charset.forName("UTF-8");
        return new String(charset.decode(readFileToBuffer(filePath)).toString());
    }

    /**
     * @param filePath
     * @return
     * @throws IOException
     */
    public static List<String> readLineFile(String filePath) throws IOException {
        BufferedReader bufReader = new BufferedReader(new FileReader(filePath));
        String tmpStr = null;
        List<String> csvDataList = new ArrayList<String>();
        while ((tmpStr = bufReader.readLine()) != null) {
            csvDataList.add(tmpStr);
        }
        bufReader.close();
        return csvDataList;
    }

    /**
     * write to file
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param fileName 기록할 파일명
     * @param b 기록할 byte의 배열
     * @throws Exception
     */
    public static void writeFile(String fileName, byte[] b) throws IOException {
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(b);
        fos.close();
    }

    /**
     * write to file
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param fileName 기록할 파일명
     * @param c 기록할 char의 배열
     * @throws Exception
     */
    public static void writeFile(String fileName, char[] c) throws IOException {
        OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileName));
        writer.write(c);
        writer.close();
    }

    /**
     * write to file
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param filePath 기록할 파일패스
     * @param content
     * @throws IOException
     */
    public static void writeFile(String filePath, String content) throws IOException {
        RandomAccessFile randomFile = null;
        ByteBuffer mapBuf = null;

        randomFile = new RandomAccessFile(filePath, "rw");
        mapBuf = randomFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, content.getBytes().length);
        mapBuf.put(content.getBytes());
        randomFile.close();
    }

    /**
     * write to file
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param filePath 기록할 파일패스
     * @param ByteBuffer inBuf 버퍼
     * @throws IOException
     */
    public static void writeFile(String filePath, ByteBuffer inBuf) throws IOException {
        RandomAccessFile randomFile = null;
        ByteBuffer mapBuf = null;

        randomFile = new RandomAccessFile(filePath, "rw");
        mapBuf = randomFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, inBuf.capacity());
        mapBuf.put(inBuf);
        randomFile.close();
    }

    /**
     * 디렉토리 경로를 인자로 받아서 디렉토리가 없을 경우 디렉토리를 생성한다.
     * 
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param homePath 기본 경로
     * @param path 상대 경로
     * @param b file 내용
     * @throws IOException
     */
    public static void writeFile(String homePath, String path, InputStream input) throws IOException {
        int lastIndex = path.lastIndexOf("/");
        if (lastIndex == -1) {
            throw new IOException("파일 경로 포맷이 잘못됐습니다: " + path);
        }

        String dirPath = path.substring(0, lastIndex);
        File dirFile = new File(homePath + dirPath);
        if (!dirFile.exists()) {
            if (!dirFile.mkdirs()) {
                throw new IOException("디렉토리 생성 실패: " + dirPath);
            }
        }

        String filePath = homePath + path;

        try {
            writeFile(filePath, input);
        } catch (IOException e) {
            throw e;
        }
    }

    /**
     * <pre>
     * Dependencies : None
     * Side Effects : None
     * </pre>
     * @param path
     * @param input
     * @throws IOException
     */
    public static void writeFile(String path, InputStream input) throws IOException {
        FileOutputStream out = new FileOutputStream(path);
        int c;
        byte[] buf = new byte[1024];
        while ((c = input.read(buf)) != -1) {
            out.write(buf, 0, c);
        }

        input.close();
        out.close();
    }
}
